package scottb.flex;

import scottb.flex.event.FlexEvent;
import scottb.flex.event.FlexEventDispatcher;
import scottb.flex.event.FlexEventListener;
import scottb.flex.event.IFlexEventDispatcher;

import javax.swing.*;
import java.awt.*;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;

public class FlexPanel extends JPanel implements FlexLayout, IFlexEventDispatcher {

    // Event dispatcher
    private FlexEventDispatcher eventDispatcher = new FlexEventDispatcher();

    // Layout and position
    private Integer top = null;
    private Integer left = null;
    private Integer bottom = null;
    private Integer right = null;
    private Integer horizontalCenter = null;
    private Integer verticalCenter = null;
    private Integer width = null;
    private Integer height = null;

    // Background
    private Color backgroundColor;
    private FlexGradient backgroundGradient;
    private Float backgroundAlpha;

    // Border
    private Color borderColor;
    private Integer borderWeight = 1;
    private Integer topLeftRadius;
    private Integer topRightRadius;
    private Integer bottomLeftRadius;
    private Integer bottomRightRadius;


    // Events
    public void addListener(FlexEventListener listener) {
        eventDispatcher.addListener(listener);
    }

    public void addListener(String type, FlexEventListener listener) {
        eventDispatcher.addListener(type, listener);
    }

    public void removeListener(FlexEventListener listener) {
        eventDispatcher.removeListener(listener);
    }

    public boolean containsListener(FlexEventListener listener) {
        return eventDispatcher.containsListener(listener);
    }

    public void dispatchEvent(FlexEvent event) {
        eventDispatcher.dispatchEvent(event);
    }

    // Layout and position
    public FlexPanel top(Integer top) {
        this.top = top;
        return this;
    }

    public Integer top() {
        return top;
    }

    public FlexPanel left(Integer left) {
        this.left = left;
        return this;
    }

    public Integer left() {
        return left;
    }

    public FlexPanel right(Integer right) {
        this.right = right;
        return this;
    }

    public Integer right() {
        return right;
    }

    public FlexPanel bottom(Integer bottom) {
        this.bottom = bottom;
        return this;
    }

    public Integer bottom() {
        return bottom;
    }

    public FlexPanel horizontalCenter(Integer center) {
        this.horizontalCenter = center;
        return this;
    }

    public Integer horizontalCenter() {
        return this.horizontalCenter;
    }

    public FlexPanel verticalCenter(Integer center) {
        this.verticalCenter = center;
        return this;
    }

    public Integer verticalCenter() {
        return this.verticalCenter;
    }

    public FlexPanel center() {
        verticalCenter(0);
        horizontalCenter(0);
        return this;
    }

    public FlexPanel width(Integer width) {
        this.width = width;
        return this;
    }

    public Integer width() {
        return width;
    }

    public FlexPanel height(Integer height) {
        this.height = height;
        return this;
    }

    public Integer height() {
        return height;
    }

    // background
    public FlexPanel background(Color color) {

        backgroundColor = color;

        return this;
    }

    public FlexPanel background(Integer color) {

        if(null != color) {
            backgroundColor = new Color(color);
        }
        else {
            backgroundColor = null;
        }

        return this;
    }

    public FlexPanel backgroundAlpha(Float alpha) {

        backgroundAlpha = alpha;

        return this;
    }

    public FlexPanel backgroundAlpha(Double alpha) {

        if(null != alpha) {
            backgroundAlpha = alpha.floatValue();
        }
        else {
            backgroundAlpha = null;
        }

        return this;
    }

    public FlexPanel gradient2(Integer color1, Integer color2) {

        gradient2(new Color(color1), new Color(color2));

        return this;
    }

    public FlexPanel gradient2(Color color1, Integer color2) {

        gradient2(color1, new Color(color2));

        return this;
    }

    public FlexPanel gradient2(Integer color1, Color color2) {

        gradient2(new Color(color1), color2);

        return this;
    }

    public FlexPanel gradient2(Color color1, Color color2) {

        backgroundGradient = new FlexGradient(color1, color2);

        return this;
    }

    // border

    public FlexPanel borderColor(Integer color) {

        if(null != color)
            this.borderColor = new Color(color);
        else
            this.borderColor = null;

        return this;
    }

    public FlexPanel borderColor(Color color) {

        this.borderColor = color;

        return this;
    }

    public FlexPanel borderWeight(Integer weight) {

        this.borderWeight = weight;

        return this;
    }

    public FlexPanel border(Integer color) {

        return borderColor(color);
    }

    public FlexPanel border(Color color) {

        return borderColor(color);
    }

    public FlexPanel border(Integer color, Integer weight) {

        borderColor(color);
        borderWeight(weight);

        return this;
    }

    public FlexPanel border(Color color, Integer weight) {

        borderColor(color);
        borderWeight(weight);

        return this;
    }

    public FlexPanel border(Integer color, Integer weight, Integer radius) {

        borderColor(color);
        borderWeight(weight);
        cornerRadius(radius);

        return this;
    }


    // corner radius
    public FlexPanel cornerRadius(Integer radius) {

        topLeftRadius = radius;
        topRightRadius = radius;
        bottomLeftRadius = radius;
        bottomRightRadius = radius;

        return this;
    }

    public FlexPanel cornerRadius(Integer topLeft, Integer topRight, Integer bottomLeft, Integer bottomRight) {

        topLeftRadius = topLeft;
        topRightRadius = topRight;
        bottomLeftRadius = bottomLeft;
        bottomRightRadius = bottomRight;

        return this;
    }

    // PAINT
    public void paint(Graphics g) {

        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        Color originalColor = g2.getColor();

        // Create background rect
        int topLeft = 0;
        int topRight = 0;
        int bottomLeft = 0;
        int bottomRight = 0;
        int width = getWidth();
        int height = getHeight();

        if(topLeftRadius != null)
            topLeft = topLeftRadius;
        if(topRightRadius != null)
            topRight = topRightRadius;
        if(bottomLeftRadius != null)
            bottomLeft = bottomLeftRadius;
        if(bottomRightRadius != null)
            bottomRight = bottomRightRadius;

        Composite originalComposite = g2.getComposite();

        if(null != backgroundAlpha) {

            Composite alphaComp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, backgroundAlpha);
            g2.setComposite(alphaComp);
        }

        // Fill background rect
        if(backgroundColor != null) {

            Shape rect = createRect(topLeft, topRight, bottomLeft, bottomRight, width, height);

            g2.setColor(backgroundColor);
            g2.fill(rect);
        }
        else if(backgroundGradient != null) {

            Shape rect = createRect(topLeft, topRight, bottomLeft, bottomRight, width, height);

            Paint backgroundPaint = new LinearGradientPaint(0, 0, 0, height, backgroundGradient.getFractions(), backgroundGradient.getColors());
            g2.setPaint(backgroundPaint);
			g2.fill(rect);
        }

        g2.setComposite(originalComposite);
        g2.setColor(originalColor);

        super.paint(g);

        // Stroke rect
        if(borderColor != null && borderWeight != null) {

            Shape rect = createRect(topLeft, topRight, bottomLeft, bottomRight, width-1, height-1);

            Stroke originalStroke = g2.getStroke();

            g2.setColor(borderColor);
		    g2.setStroke(new BasicStroke(borderWeight));
            g2.draw(rect);

            g2.setStroke(originalStroke);

        }
    }

    private Shape createRect(int topLeft, int topRight, int bottomLeft, int bottomRight, int width, int height) {

        GeneralPath path = new GeneralPath();
        path.moveTo(topLeft, 0);
        path.lineTo(width - topRight, 0);
        path.curveTo(width, 0, width, 0, width, topRight);
        path.lineTo(width, height - bottomRight);
        path.curveTo(width, height, width, height, width - bottomRight, height);
        path.lineTo(bottomLeft, height);
        path.curveTo(0, height, 0, height, 0, height - bottomLeft);
        path.lineTo(0, topLeft);
        path.curveTo(0, 0, 0, 0, topLeft, 0);

        return path;
    }
}
